package net.asfun.jvalog.mvc;

import static net.asfun.jvalog.common.log.logger;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.asfun.jvalog.vo.Setting;


public class DispatcherServlet extends HttpServlet {

	private static final long serialVersionUID = -664265160696927922L;
	private List<HandlerInterceptor> interceptors;
	private Map<String, HandlerController> controllers = new HashMap<String, HandlerController>();
	private HandlerExceptionResolver resolver;
	private Class<ModelAndView> mavClass;
	
	@SuppressWarnings("unchecked")
	@Override
	public void init(ServletConfig config) throws ServletException {
		String name = config.getServletName();
		super.init(config);
		//set root
		String webRoot = getServletContext().getRealPath("/");
		logger.fine("DispatcherServlet [" + name +"] war dir is :" + webRoot);
		Setting.WEB_ROOT = webRoot;
		//set exceptionResolver
		String exceptionResolver = config.getInitParameter("exceptionResolver");
		if ( exceptionResolver != null ) {
			try {
				resolver = (HandlerExceptionResolver) Class.forName(exceptionResolver).newInstance();
				logger.info("DispatcherServlet [" + name +"] set exceptionResolver :" + exceptionResolver);
			} catch (Exception e) {
				logger.log(Level.SEVERE, 
						"DispatcherServlet [" + name +"] cause exception " + e.getMessage() + " while setting exceptionResolver :" + exceptionResolver,
						e.getCause());
				throw new ServletException(e.getMessage(), e.getCause());
			}
		}
		//set interceptors
		String handlerInterceptors = config.getInitParameter("handlerInterceptors");
		if ( handlerInterceptors != null ) {
			String his[] = handlerInterceptors.split(",");
			interceptors = new ArrayList<HandlerInterceptor>(his.length);
			String hi = null;
			try {
				for(int i=0; i<his.length; i++) {
					hi = his[i];
					interceptors.add((HandlerInterceptor) Class.forName(hi.trim()).newInstance());
					logger.info("DispatcherServlet [" + name +"] add handlerInterceptor :" + hi.trim());
				}
			} catch (Exception e) {
				logger.log(Level.SEVERE, 
						"DispatcherServlet [" + name +"] cause exception " + e.getMessage() + " while adding handlerInterceptor :" + hi,
						e.getCause());
				throw new ServletException(e.getMessage(), e.getCause());
			}
		} else {
			interceptors = new ArrayList<HandlerInterceptor>(0);
		}
		//set controllers
		String conts = config.getInitParameter("controllers");
		if ( conts == null ) {
			logger.severe("DispatcherServlet [" + name +"] need 'controllers' initParam to be setup.");
			throw new ServletException("Init param 'controllers' must be setup.");
		}
		String[] cons = conts.split(",");
		String con = null;
		try {
			for(int i=0; i<cons.length; i++) {
				con = cons[i];
				Object controller = Class.forName(con.trim()).newInstance();
				logger.info("DispatcherServlet [" + name +"] building mapping from :" + con.trim());
				controllers.putAll(MappingAnnotationResolver.search(controller));
			}
		} catch (Exception e) {
			logger.log(Level.SEVERE, 
					"DispatcherServlet [" + name +"] cause exception " + e.getMessage() + " while adding controller :" + con,
					e.getCause());
			throw new ServletException(e.getMessage(), e.getCause());
		}
		//set view
		String view = config.getInitParameter("view");
		if ( view == null ) {
			logger.severe("DispatcherServlet [" + name +"] need 'view' initParam to be setup.");
			throw new ServletException("Init param 'view' must be setup.");
		}
		try {
			mavClass =  (Class<ModelAndView>) Class.forName(view);
			logger.info("DispatcherServlet [" + name +"] set viewClass :" + view);
		} catch (Exception e) {
			logger.log(Level.SEVERE, 
					"DispatcherServlet [" + name +"] cause exception " + e.getMessage() + " while setting view :" + view,
					e.getCause());
			throw new ServletException(e.getMessage(), e.getCause());
		}
	}
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException{
		long ts = System.currentTimeMillis();
		String servletPath = req.getServletPath();
		logger.fine("servletPath=" + servletPath);
		String uri = req.getRequestURI();
		logger.fine("reqestUri=" + uri);
		//get controller
		HandlerController controller = controllers.get(uri.replaceFirst(servletPath, ""));
		if ( controller == null ) {
			controller = controllers.get(uri);
			if ( controller == null ) {
				controller = controllers.get("*");
				if ( controller == null ) {
					resp.sendError(404);
				}
			}
		}
		//handle
		Exception ane = null;
		try {
			// intercept pre
			for ( HandlerInterceptor inc : interceptors) {
				if ( ! inc.preHandle(req, resp, controller)) {
					return;
				}
			}
			// controller
			ModelAndView mav = null;
			try {
				mav = controller.handler(mavClass, req, resp);
			} catch (Exception e) {
				// exception resolve
				if ( resolver == null ) {
					throw e;
				}
				mav = resolver.resolveException(req, resp, controller, e);
				if ( mav == null ) {
					throw new Exception("Handler exception has no response.");
				}
			}
			// intercept post
			for ( HandlerInterceptor inc : interceptors ) {
				inc.postHandle(req, resp, controller, mav);
			}
			// mav render
			mav.render(req, resp);
		} catch (Exception e) {
			ane = e;
		} finally {
			// intercept cop
			try {
				for ( HandlerInterceptor inc : interceptors ) {
					inc.afterCompletion(req, resp, controller, ane);
				}
			} catch (Exception ex) {
				logger.log(Level.SEVERE, "dispatch handler meet error.", ex.getCause());
				throw new IOException(ex.getMessage(), ex.getCause());
			}
		}
		System.out.println("Cost time = " + (System.currentTimeMillis() - ts));
	}
	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException{
		doGet(req, resp);
	}
}
